package service

import (
	"context"
	"database/sql"
	"go-demo/domain"
	"go-demo/util"
	"time"
)

type AuditLogDto struct {
	Id                string               `json:"id"`                //ID
	ApplicationName   *string              `json:"applicationName"`   //应用程序名和曾
	UserId            *string              `json:"userId"`            //用户ID
	UserName          *string              `json:"userName"`          //用户名称
	ExecutionTime     time.Time            `json:"executionTime"`     //执行时间开始
	ExecutionDuration int                  `json:"executionDuration"` //执行耗时
	ClientIpAddress   *string              `json:"clientIpAddress"`   //客户端IP地址
	ClientName        *string              `json:"clientName"`        //客户端名和曾
	ClientId          *string              `json:"clientId"`          //客户端ID
	BrowserInfo       *string              `json:"browserInfo"`       //浏览器详情
	HttpMethod        *string              `json:"httpMethod"`        //HTTP方法
	Url               *string              `json:"url"`               //URL
	Exceptions        *string              `json:"exceptions"`        //异常信息
	HttpStatusCode    *int                 `json:"httpStatusCode"`    //HTTP状态码
	Actions           []*AuditLogActionDto `json:"actions"`           //动作
	EntityChanges     []*EntityChangeDto   `json:"entityChanges"`     //实体变更
}

type AuditLogActionDto struct {
	Id                string    `json:"id"`                //ID
	AuditLogId        string    `json:"auditLogId"`        //
	ServiceName       *string   `json:"serviceName"`       //服务名称
	MethodName        *string   `json:"methodName"`        //方法名称
	Parameters        *string   `json:"parameters"`        //参数列表
	ExecutionTime     time.Time `json:"executionTime"`     //执行时间
	ExecutionDuration int       `json:"executionDuration"` //执行耗时
}

type EntityChangeDto struct {
	Id                 string                     `json:"id"`                 //ID
	AuditLogId         string                     `json:"auditLogId"`         //
	ChangeTime         time.Time                  `json:"changeTime"`         //变更时间
	ChangeType         int                        `json:"changeType"`         //变更类型
	EntityId           string                     `json:"entityId"`           //
	EntityTypeFullName string                     `json:"entityTypeFullName"` //实体类型全称
	PropertyChanges    []*EntityPropertyChangeDto `json:"propertyChanges"`    //属性变更
}

type EntityPropertyChangeDto struct {
	Id                   string  `json:"id"`
	EntityChangeId       string  `json:"entityChangeId"`
	NewValue             *string `json:"newValue"`
	OriginalValue        *string `json:"originalValue"`
	PropertyName         string  `json:"propertyName"`
	PropertyTypeFullName string  `json:"propertyTypeFullName"`
}

type GetListAuditLogInput struct {
	PageAndSortRequest
	UserName           string    `form:"userName" required:"1"`                                  //用户名称
	ExecutionTimeBegin time.Time `form:"executionTimeBegin" default:"2000-01-01T00:00:00+08:00"` //执行时间开始
	ExecutionTimeEnd   time.Time `form:"executionTimeEnd" default:"9999-01-01T00:00:00+08:00"`   //执行时间结束
	ClientIpAddress    string    `form:"clientIpAddress"`                                        //客户端IP地址
	HttpMethod         string    `form:"httpMethod" example:"GET|POST|PUT|DELETE"`               //HTTP方法
	Url                string    `form:"url"`                                                    //URL
	HttpStatusCode     int       `form:"httpStatusCode"`                                         //HTTP状态码
}

type GetListAuditLogOutput struct {
	PageResponse
	Items []*AuditLogDto `json:"items"`
}

type AuditLogAppService struct {
	uow                *domain.UnitOfWork
	auditLogRepository *domain.AuditLogRepository
}

func NewAuditLogAppService(uow *domain.UnitOfWork, auditLogRepository *domain.AuditLogRepository) *AuditLogAppService {
	return &AuditLogAppService{uow, auditLogRepository}
}

func (s *AuditLogAppService) GetList(ctx context.Context, input *GetListAuditLogInput) (output *GetListAuditLogOutput, code int, err error) {
	output = new(GetListAuditLogOutput)

	//获取数据
	list, count, err := s.auditLogRepository.GetList(ctx, input.UserName, input.HttpMethod, input.HttpStatusCode, input.ClientIpAddress, input.ExecutionTimeBegin, input.ExecutionTimeEnd, input.Url, input.Sorting, input.PageIndex, input.PageSize)
	if err != nil {
		return nil, util.StatusErr, err
	}

	//装载DTO
	output.Items = make([]*AuditLogDto, 0)
	for _, v := range list {
		output.Items = append(output.Items, auditLogMapToDto(v))
	}
	output.TotalCount = count

	return output, util.StatusOk, err
}

func (s *AuditLogAppService) GetOne(ctx context.Context, id string) (*AuditLogDto, int, error) {
	entity, err := s.auditLogRepository.Get(ctx, id, true)

	if err != nil {
		if err == sql.ErrNoRows {
			return nil, util.StatusFindNotFound, nil
		}
		return nil, util.StatusErr, err
	}

	dto := auditLogMapToDto(entity)

	return dto, util.StatusOk, err
}

func auditLogMapToDto(entity *domain.AuditLog) *AuditLogDto {
	dto := &AuditLogDto{
		Id:                entity.Id,
		ApplicationName:   entity.ApplicationName,
		UserId:            entity.UserId,
		UserName:          entity.UserName,
		ExecutionTime:     entity.ExecutionTime,
		ExecutionDuration: entity.ExecutionDuration,
		ClientIpAddress:   entity.ClientIpAddress,
		ClientName:        entity.ClientName,
		ClientId:          entity.ClientId,
		BrowserInfo:       entity.BrowserInfo,
		HttpMethod:        entity.HttpMethod,
		Url:               entity.Url,
		Exceptions:        entity.Exceptions,
		HttpStatusCode:    entity.HttpStatusCode,
		Actions:           make([]*AuditLogActionDto, 0),
		EntityChanges:     make([]*EntityChangeDto, 0),
	}

	for _, v := range entity.Actions {
		dto.Actions = append(dto.Actions, &AuditLogActionDto{
			Id:                v.Id,
			AuditLogId:        v.AuditLogId,
			ServiceName:       v.ServiceName,
			MethodName:        v.MethodName,
			Parameters:        v.Parameters,
			ExecutionTime:     v.ExecutionTime,
			ExecutionDuration: v.ExecutionDuration,
		})
	}

	for _, v := range entity.EntityChanges {
		entityChangeDto := &EntityChangeDto{
			Id:                 v.Id,
			AuditLogId:         v.AuditLogId,
			ChangeTime:         v.ChangeTime,
			ChangeType:         v.ChangeType,
			EntityId:           v.EntityId,
			EntityTypeFullName: v.EntityTypeFullName,
			PropertyChanges:    make([]*EntityPropertyChangeDto, 0),
		}
		for _, v2 := range v.PropertyChanges {
			entityChangeDto.PropertyChanges = append(entityChangeDto.PropertyChanges, &EntityPropertyChangeDto{
				Id:                   v2.Id,
				EntityChangeId:       v2.EntityChangeId,
				NewValue:             v2.NewValue,
				OriginalValue:        v2.OriginalValue,
				PropertyName:         v2.PropertyName,
				PropertyTypeFullName: v2.PropertyTypeFullName,
			})
		}
		dto.EntityChanges = append(dto.EntityChanges, entityChangeDto)
	}

	return dto
}
